<?php


namespace App\Utils\Wechat;


use App\Constants\ErrorCode;
use App\Exception\BusinessException;
use App\Utils\Common;
use App\Utils\Log;
use EasyWeChat\Kernel\Support\Collection;
use EasyWeChat\Payment\Application;
use Hyperf\Server\Exception\RuntimeException;
use Psr\Http\Message\ResponseInterface;

class Payment extends Base
{
    const TRADE_TYPE_JSAPI = 'JSAPI'; // 小程序、公众号交易
    const TRADE_TYPE_NATIVE = 'NATIVE';  // 网页扫码交易
    const TRADE_TYPE_APP = 'APP';  // APP 交易
    const TRADE_TYPE_MWEB = 'MWEB';  // H5 交易

    public function __construct(array $config = [])
    {
//        $this->config = $config ?? config('payment.wechat');
        $this->config = count($config) > 0 ? $config : config('payment.wechat');

//        var_dump($this->config);
        $this->app = make(Application::class, [$this->config]);
        parent::__construct();
    }

    /**
     * @param int|float $money 交易金额
     * @param string $tradeType 交易类型
     * @param string $openID 用户openID，JSAPI 交易类型下必传
     * @param string $notifyUrl 回调地址
     * @param bool $refresh 是否强制生成新的订单
     * @return array|Collection|object|ResponseInterface|string
     */
    public function prepay($money, string $tradeType, string $openID = null, string $notifyUrl = null, $refresh = false)
    {
        $prepayKey = $this->prepayKey($openID, $money, $tradeType);

        if (!$refresh && $this->redis->exists($prepayKey)) {
            return json_decode($this->redis->get($prepayKey), true);
        }

        if ($notifyUrl == null) {
            $notifyUrl = $this->config['notify_url'];
        }

        try {
            $this->app->request->headers = [
                'Referer' => "http://98gongyi.com/"
            ];
            $res = $this->app->order->unify(
                [
                    'body' => '优客平台充值',
                    'out_trade_no' => $this->tradeNo(),
                    'total_fee' => bcmul($money, 100),
                    'notify_url' => $notifyUrl, // 支付结果通知网址，如果不设置则会使用配置里的默认地址
                    'trade_type' => $tradeType, // 请对应换成你的支付方式对应的值类型
                    'openid' => $openID,
                    'time_expire' => date('YmdHis', time() + 1800) // 设置订单半个小时后失效
                ]
            );
            Log::get()->info(json_encode($res));
        } catch (\Throwable $e) {
            Log::get()->error($e->getTraceAsString());
            throw new RuntimeException($e->getMessage(), $e->getCode());
        }

        if (strtoupper($res['return_msg']) == 'OK' && strtoupper($res['return_code']) == 'SUCCESS') {
            if ($tradeType == self::TRADE_TYPE_JSAPI) {
                /**
                 * TRADE_TYPE_JSAPI - 微信调用参数需要处理下
                 */
                if (!isset($res['prepay_id'])) {
                    Log::get()->error(-1, [$res]);
                    throw new BusinessException(ErrorCode::PAYMENT_ERROR);
                }

                $res = $this->app->jssdk->sdkConfig($res['prepay_id']);
                $res['timeStamp'] = (string)$res['timestamp'];
                unset($res['timestamp']);
            }
            $this->redis->set($prepayKey, json_encode($res), 1800 + 300);
        }
        return $res;
    }

    /**
     * 获取交易单号
     * @return int|null
     */
    public function tradeNo()
    {
        return Common::getRequestID();
    }

    /**
     * 获取订单 redis key
     * @param $openID
     * @param $money
     * @param $tradeType
     * @return string
     */
    public function prepayKey($openID, $money, $tradeType)
    {
        return md5(sprintf('%s:%s:%s', $openID, (string)$money, $tradeType));
    }

    /**
     * 关联用户
     * @param $prepayKey
     * @param $userId
     */
    public function setBindUser($prepayKey, $userId)
    {
        $this->redis->set($prepayKey . ':user_id', $userId, 1800 + 300);
    }

    /**
     * 获取用户id
     * @param $prepayKey
     * @return bool|mixed|string
     */
    public function getBindUser($prepayKey)
    {
        return $this->redis->get($prepayKey . ':user_id');
    }

    /**
     * 删除关联
     * @param $prepayKey
     * @return int
     */
    public function delBindUser($prepayKey)
    {
        return $this->redis->del($prepayKey . ':user_id');
    }

    /**
     * 交易完成后，释放订单
     * @param $prepayKey
     * @return int
     */
    public function release($prepayKey)
    {
        return $this->redis->del($prepayKey);
    }

}